#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <map>
#include <vector>

using namespace std;
using LL = long long;
const int N = 2010;

int n;
vector<int> e[N];
int p[N], c[N], a[N];
/*
因为对于一个子树来说，c[i]代表的是i节点的子树中有多少个点小于a[i],也就是说是相对大小，因此只要不改变相对大小就好了
用vector存储子树，vector存储的值是节点编号，而节点的值是节点的下标
对于每个节点，子树填数，比如该节点子树有10个点，c[x]是5（有五个节点比这个节点的权小）
那么根据bfs回溯，先把子节点确定，返回前四个整合好的vector
然后该点因为c[x]是5，选vector前5个较小的，添加自己的位置，再把后面的位置填数

（注意理解，返回的vector存的是每个点的编号，而位置才是真正的数，利用了vector增长时候新增加的点会是所有位置+1的性质）
怎么判断错误？一个点的子树返回的vector不满足c[i]，比如c[i]是100，子节点只有50个点，要比100个点大，显然不成立，exit(0)

*/

vector<int> dfs(int u){
    vector<int> temp;
    for(int i = 0; i < e[u].size(); i ++){
        vector<int> t = dfs(e[u][i]);
        for(int j = 0; j < t.size(); j ++){
            temp.push_back(t[j]);
        }
    }

    if(u == 0) return temp;
    if(c[u] > temp.size()){
        cout << "NO" << '\n';
        exit(0);
    }
    vector<int> ans;
    for(int i = 0; i < c[u]; i ++) ans.push_back(temp[i]);
    ans.push_back(u);
    for(int i = c[u]; i < temp.size(); i ++) ans.push_back(temp[i]);

    return ans;
}


int main(){
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n;
    for(int i = 1, fa; i <= n; i ++){
        cin >> fa >> c[i];
        e[fa].push_back(i);
    }

    vector<int> ans = dfs(0);

    for(int i = 0; i < ans.size(); i ++){
        a[ans[i]] = i + 1;
    }

    cout << "YES" << '\n';
    for(int i = 1; i <= ans.size(); i ++) cout << a[i] << " ";


    return 0;
}